package kam.fire;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.TimeZone;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;

/**
 * the Kamifre instance handles all HTTP connections as well as a few utilities
 * such as gif handling, sdcard interactions, and information caching
 * 
 * @author palepail
 * 
 */
public class KamfireInstance {
	String site = "";
	String apiToken = "";
	private User user = new User();
	private ArrayList<Room> rooms = new ArrayList<Room>();
	private ArrayList<Message> messages = new ArrayList<Message>();
	private ArrayList<Upload> uploads = new ArrayList<Upload>();
	private BufferedInputStream gifStream;

	/**
	 * creates a new kamfire instance that stores the site and apitoken
	 * 
	 * @param site
	 * @param apiToken
	 */
	public KamfireInstance(String site, String apiToken) {
		this.site = site;
		this.apiToken = apiToken;
	}

	/**
	 * returns the user object
	 * 
	 * @return
	 */
	// =====================Data Getters
	public User getUser() {
		return user;
	}

	/**
	 * returns the list of uploads
	 * 
	 * @return
	 */
	public ArrayList<Upload> getUploads() {
		return uploads;
	}

	/**
	 * returns the apiToken
	 * 
	 * @return
	 */
	public String getApiToken() {
		return apiToken;
	}

	/**
	 * returns the campsite
	 * 
	 * @return
	 */
	public String getCampSite() {
		return site;
	}

	/**
	 * returns the rooms
	 * 
	 * @return
	 */
	public ArrayList<Room> getRooms() {
		return rooms;
	}

	/**
	 * returns the messages
	 * 
	 * @return
	 */
	public ArrayList<Message> getMessages() {
		return messages;
	}

	/* Returns true if the Login was successful. Otherwise returns false */

	// =======================Action Initiators
	/**
	 * attempts the login to campfire. return true if successful
	 * 
	 * @return
	 */
	public boolean login() {

		String response = loginToCampfire("users/me");
		if (response != "") {
			try {
				user = JsonHandler.getObjectFromJSON(response).getUser();
			} catch (Exception e) {
				return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * retrieves list of rooms from campfire. returns true if successful
	 * 
	 * @return
	 */
	public boolean populateRooms() {
		String response = getRoomsFromCampfire("rooms");
		if (response != "") {
			try {
				rooms = JsonHandler.getObjectFromJSON(response).getRooms();
			} catch (Exception e) {
				return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * fetches all messages since the last message in the database in a given
	 * room. returns true if successful
	 * 
	 * @param roomId
	 * @param lastMessage
	 * @return
	 */
	public boolean populateMessages(int roomId, int lastMessage) {
		String response = getMessagesFromCampfire("room/" + roomId + "/recent", lastMessage);
		if (response != "") {
			try {
				messages = JsonHandler.getObjectFromJSON(response).getMessages();
			} catch (Exception e) {
				return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * attempts to post a message to campfire. returns true if successful
	 * 
	 * @param roomId
	 * @param message
	 * @return
	 */
	public boolean postTextMessage(int roomId, Message message) {
		String response = postMessageToCampfire("room/" + roomId + "/speak", JsonHandler.getJsonFromMessage(message));
		if (response != "") {
			try {
				messages.add(JsonHandler.getObjectFromJSON(response).getMessage());
			} catch (Exception e) {
				return false;
			}
			return true;
		}
		return false;
	}

	/**
	 * attempts to fetch an upload object from campfire. returns the upload if
	 * successful. null if not
	 * 
	 * @param roomId
	 * @param messageId
	 * @return
	 */
	public Upload getUploadRequest(int roomId, int messageId) {
		String response = getUploadFromCampfire("room/" + roomId + "/messages" + "/" + messageId + "/upload");
		Upload upload = new Upload();
		if (response != "") {
			try {
				upload = JsonHandler.getObjectFromJSON(response).getUpload();
			} catch (Exception e) {
				return null;
			}
			return upload;
		}
		return null;
	}

	/**
	 * attempts to post an image file to campfire. returns the upload object if
	 * successful
	 * 
	 * @param roomId
	 * @param filePath
	 * @return
	 */
	public Upload postImageUpload(int roomId, String filePath) {
		String response = postImageToCampfire("room/" + roomId + "/uploads", filePath);
		Upload upload = new Upload();
		if (response != "") {
			try {
				upload = JsonHandler.getObjectFromJSON(response).getUpload();
			} catch (Exception e) {
				return null;
			}
			return upload;
		}
		return null;
	}

	// =================Action Hubs
	private String loginToCampfire(String action) {
		URI uri = makeGenericURI(action);
		HttpResponse httpResponse = getHttpResponse(uri);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	private String getMessagesFromCampfire(String action, int lastMessage) {

		URI uri = makeMessagesURI(action, lastMessage);
		HttpResponse httpResponse = getHttpResponse(uri);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	private String postMessageToCampfire(String action, String jsonFromMessage) {
		URI uri = makeGenericURI(action);
		HttpResponse httpResponse = getHttpPostMessageResponse(uri, jsonFromMessage);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	private String getUploadFromCampfire(String action) {
		URI uri = makeGenericURI(action);
		HttpResponse httpResponse = getHttpResponse(uri);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	private String getRoomsFromCampfire(String action) {
		URI uri = makeGenericURI(action);
		HttpResponse httpResponse = getHttpResponse(uri);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	private String postImageToCampfire(String action, String filePath) {
		URI uri = makeGenericURI(action);
		ArrayList<NameValuePair> imageData = makeImageData(filePath);
		HttpResponse httpResponse = getHttpPostImageResponse(uri, imageData);
		String response = getStringFromResponse(httpResponse);
		return response;
	}

	// =================Get Http Response

	private HttpResponse getHttpResponse(URI uri) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet = new HttpGet();
		httpGet.setURI(uri);
		httpGet.addHeader(getHeader());
		HttpResponse httpResponse = null;
		try {
			httpResponse = httpClient.execute(httpGet);
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return httpResponse;
	}

	private HttpResponse getHttpPostImageResponse(URI uri, ArrayList<NameValuePair> imageData) {
		HttpClient httpClient = new DefaultHttpClient();
		HttpContext localContext = new BasicHttpContext();
		HttpResponse response = null;
		HttpPost httpPost = new HttpPost();

		httpPost.setURI(uri);
		httpPost.addHeader(getHeader());

		try {
			MultipartEntity entity = new MultipartEntity();

			for (int index = 0; index < imageData.size(); index++) {
				if (imageData.get(index).getName().equalsIgnoreCase("upload")) {
					// If the key equals to "upload", we use FileBody to
					// transfer the data
					entity.addPart(imageData.get(index).getName(), new FileBody(new File(imageData.get(index)
							.getValue())));
				} else {
					// Normal string data
					entity.addPart(imageData.get(index).getName(), new StringBody(imageData.get(index).getValue()));
				}
			}

			httpPost.setEntity(entity);
			response = httpClient.execute(httpPost, localContext);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return response;
	}

	private HttpResponse getHttpPostMessageResponse(URI uri, String json) {
		HttpPost httpPost = new HttpPost();
		HttpClient httpClient = new DefaultHttpClient();
		httpPost.setURI(uri);
		HttpResponse httpResponse = null;
		try {
			httpPost.setEntity(new StringEntity(json));

			httpPost.setHeader("Accept", "application/json");
			httpPost.setHeader("Content-type", "application/json");
			httpPost.addHeader(getHeader());

			httpResponse = httpClient.execute(httpPost);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return httpResponse;
	}

	// ===================== Make URIs

	private URI makeGenericURI(String action) {
		URI uri = null;
		try {
			uri = new URI("https://" + site + ".campfirenow.com/" + action + ".json");
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return uri;
	}

	private URI makeMessagesURI(String action, int lastMessage) {

		URI uri = null;
		try {
			uri = new URI("https://" + site + ".campfirenow.com/" + action + ".json?since_message_id=" + lastMessage);
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return uri;
	}

	// =====================Http Utilites
	private ArrayList<NameValuePair> makeImageData(String filePath) {
		ArrayList<NameValuePair> imageData = new ArrayList<NameValuePair>();
		imageData.add(new BasicNameValuePair("upload", filePath));
		return imageData;
	}

	private InputStream getInputStream(String url) {
		try {
			HttpClient httpClient = new DefaultHttpClient();
			HttpResponse httpResponse;
			HttpGet httpGet = new HttpGet();
			URI uri = new URI(url);

			httpGet.setURI(uri);
			httpGet.addHeader(getHeader());

			httpResponse = httpClient.execute(httpGet);
			if (httpResponse.getStatusLine().getStatusCode() == 200
					|| httpResponse.getStatusLine().getStatusCode() == 201) {
				HttpEntity httpEntity = httpResponse.getEntity();

				InputStream is = (InputStream) httpEntity.getContent();
				return is;
			}
			return null;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	private String getStringFromResponse(HttpResponse httpResponse) {
		StringBuffer stringBuffer = new StringBuffer("");
		BufferedReader bufferedReader = null;

		if (httpResponse.getStatusLine().getStatusCode() == 200 || httpResponse.getStatusLine().getStatusCode() == 201) {
			InputStream inputStream;
			try {
				inputStream = httpResponse.getEntity().getContent();

				bufferedReader = new BufferedReader(new InputStreamReader(inputStream));

				String readLine = bufferedReader.readLine();
				while (readLine != null) {
					stringBuffer.append(readLine);
					stringBuffer.append("\n");
					readLine = bufferedReader.readLine();
				}
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		} else {
			return "";
		}

		return stringBuffer.toString();

	}

	private Header getHeader() {
		return BasicScheme.authenticate(new UsernamePasswordCredentials(apiToken, "ThisDoesNotMatter"), HTTP.UTF_8,
				false);
	}
	//=============================Message conversions
	
	@SuppressLint("SimpleDateFormat")
	public String convertDate(String date){
		DateFormat odf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss Z");
		DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
	    Date startDate;
	    String newDateString="";
	    try {
	        startDate = odf.parse(date);
	        TimeZone timeZone = TimeZone.getDefault();
	        df.setTimeZone(timeZone);
	        newDateString = df.format(startDate);
	    } catch (ParseException e) {
	        e.printStackTrace();
	    }
		
		return newDateString;
	}
	// ============================Image and Gif Methods
	/**
	 * assigns the gifStream from a url and expected filesize
	 * 
	 * @param url
	 * @param filesize
	 */
	public void setGifStream(String url, int filesize) {
		BufferedInputStream bis = new BufferedInputStream(getInputStream(url));
		bis.mark(filesize);
		gifStream = bis;
	}
	
	public void setGifStreamFromFile(File file) {
		BufferedInputStream bin = null;
		try {
			bin = new BufferedInputStream(new FileInputStream(file));
			bin.mark((int)file.length());
			gifStream = bin;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
	}

	/**
	 * returns the gifStream
	 * 
	 * @return
	 */
	public BufferedInputStream getGifStream() {
		return gifStream;
	}

	/**
	 * returns the drawable created from a url contains an if checking the android version, uses deprecated code if the 
	 * version is below honeycomb. changes scale to .75 of the screen size.
	 * 
	 * @param url
	 * @return
	 */
	@SuppressLint("NewApi")
	@SuppressWarnings("deprecation")
	public Bitmap loadImageFromWeb(String url, Context context) {

		// First decode with inJustDecodeBounds=true to check dimensions
		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeStream(getInputStream(url), null, options);
		
		// Get screen size
		int height;
		int width;
		@SuppressWarnings("static-access")
		WindowManager wm = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
		Display display = wm.getDefaultDisplay();
		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
			width = display.getWidth();
			height = display.getHeight();

		} else {
			Point size = new Point();
			display.getSize(size);
			width = size.x;
			height = size.y;
		}

		// Calculate inSampleSize
		// options.inSampleSize = calculateInSampleSize(options, width, height);

		int scale = 1;
		while (options.outWidth / scale / 2 >= width*.75 || options.outHeight / scale / 2 >= height*.75) {
			scale *= 2;
		}

		BitmapFactory.Options options2 = new BitmapFactory.Options();
		options2.inSampleSize = scale;

		// Decode bitmap with inSampleSize set
		
		return BitmapFactory.decodeStream(getInputStream(url), null, options2);

	}
	

	public Bitmap loadUnscaledImageFromWeb(String imageUrl) {
		
		return  BitmapFactory.decodeStream(getInputStream(imageUrl));
	}


	// =====================File Saving
	/**
	 * saves data to sdcard in a folder kamfire given an upload. uses upload url
	 * and upload name returns true if successful
	 * 
	 * @param upload
	 * @return
	 */
	public boolean saveDataFile(Upload upload) {
		boolean success = false;
		try {

			File storagePath = getStoragePath();
			File file = new File(storagePath, getSavedUploadName(upload));
			InputStream input = new BufferedInputStream(getInputStream(upload.getUrl()));
			OutputStream output = new FileOutputStream(file);

			byte data[] = new byte[1024];
			long total = 0;
			int count = 0;
			while ((count = input.read(data)) != -1) {
				total++;
				Log.e("while", "A" + total);

				output.write(data, 0, count);
			}

			output.flush();
			output.close();
			input.close();
			success = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return success;
	}

	/**
	 * saves an image to the sdcard to the folder kamfire provided that the
	 * image has already been displayed. uses upload for naming
	 * 
	 * @param upload
	 * @param drawable
	 * @return
	 */
	public boolean saveImageFile(Upload upload, BitmapDrawable drawable) {
		// TODO Auto-generated method stub
		boolean success = false;
		String fileExtension = getFileExtension(upload);

		Bitmap bitmap = drawable.getBitmap();
		File storagePath = getStoragePath();
		File image = new File(storagePath, getSavedUploadName(upload));

		// Encode the file as a PNG image.
		FileOutputStream outStream;
		try {

			outStream = new FileOutputStream(image);
			if (fileExtension.equals(".jpg")) {
				bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
			} else { // (fileExtension.equals(".png")) {
				bitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
			}
			/* 100 to keep full quality of the image */

			outStream.flush();
			outStream.close();
			success = true;
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return success;
	}

	/**
	 * returns the file extension given the file type field of an upload object
	 * 
	 * @param upload
	 * @return
	 */
	public String getFileExtension(Upload upload) {
		String ex = upload.getName().substring(upload.getName().length() - 4);
		if (upload.getType().equals("image/jpg")) {
			ex = ".jpg";
		}
		if (upload.getType().equals("image/png")) {
			ex = ".png";
		}
		if (upload.getType().equals("image/gif")) {
			ex = ".gif";
		}
		return ex;
	}

	private String getSavedUploadName(Upload upload) {
		return upload.getId() + "_" + upload.getName();
	}

	private File getStoragePath() {
		File storagePath = new File(Environment.getExternalStorageDirectory(), "Kamfire");
		if (!storagePath.exists())
			storagePath.mkdirs();

		return storagePath;
	}

	/**
	 * checks the sd card if a file has been previously downloaded. checks
	 * agains the message id
	 * 
	 * @param id
	 * @return
	 */
	public File getDownloadedFile(int id) {
		// boolean there = false;
		File theFile = null;
		File[] files = null;
		String ID = Integer.toString(id);
		files = getStoragePath().listFiles();
		if (files != null) {
			for (File file : files) {
				if (file.getName().startsWith(ID)) {
					theFile = file;
					// there=true;
					break;
				}
			}
		}
		return theFile;
	}

}